package com.bnzj.core.persistence.query;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import com.bnzj.core.persistence.entity.BaseEntity;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.CollectionUtils;

/**
 * 规范化查询
 * @author barry
 *
 * @param <T>
 */
public class QuerySpecification<T extends BaseEntity> implements Specification<T> {

	private static final long serialVersionUID = 7483195597335855986L;
	
	private transient List<CompareExpression> criteriaList = new ArrayList<CompareExpression>();
	
	public QuerySpecification(List<CompareExpression> criteriaList) {
		this.criteriaList = criteriaList;
	}

	@Override
	public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
		if (!CollectionUtils.isEmpty(criteriaList)) {
			Predicate[] predicates = new Predicate[criteriaList.size()];
			for (int i = 0; i < criteriaList.size(); i++) {
			    CompareExpression expression = criteriaList.get(i);
				String stringValue = null;
				Number numberValue = null;
				Date dateValue = null;
				Collection<?> collectionValue = null;
				if (expression.getValue() instanceof String) {
					stringValue = (String) expression.getValue();
				} else if (expression.getValue() instanceof Number) {
					numberValue = (Number) expression.getValue();
				} else if (expression.getValue() instanceof Date) {
					dateValue = (Date) expression.getValue();
				} else if (expression.getValue() instanceof Collection) {
					collectionValue = (Collection<?>) expression.getValue();
				}
				CompareType compareType = expression.getCompareType();
				CompareOperator compareOperator = compareType.getCompareOperator();
				//TODO 需要改成反射
                switch (compareOperator) {
                    case in:
                        predicates[i] = criteriaBuilder.in(root.get(expression.getField())).in(collectionValue);
                        break;
                    case startsWith:
                        predicates[i] = criteriaBuilder.like(root.get(expression.getField()), stringValue + "%");
                        break;
                    case endWith:
                        predicates[i] = criteriaBuilder.like(root.get(expression.getField()), "%" + stringValue);
                        break;
                    case like:
                        predicates[i] = criteriaBuilder.like(root.get(expression.getField()), "%" + stringValue + "%");
                        break;
                    case between:
                        if (stringValue != null) {
                            predicates[i] = criteriaBuilder.between(root.get(expression.getField()), stringValue, (String)expression.getValue2());
                        } else if (dateValue != null) {
                            predicates[i] = criteriaBuilder.between(root.get(expression.getField()), dateValue, (Date)expression.getValue2());
                        } else if (numberValue != null) {
                            predicates[i] = criteriaBuilder.between(root.get(expression.getField()), numberValue.intValue(), ((Number)expression.getValue2()).intValue());
                        }
                        break;
                    case equal:
                        if (stringValue != null) {
                            predicates[i] = criteriaBuilder.equal(root.get(expression.getField()), stringValue);
                        } else if (dateValue != null) {
                            predicates[i] = criteriaBuilder.equal(root.get(expression.getField()), dateValue);
                        } else if (numberValue != null) {
                            predicates[i] = criteriaBuilder.equal(root.get(expression.getField()), numberValue);
                        }
                        break;
                    case greaterThan:
                        if (stringValue != null) {
                            predicates[i] = criteriaBuilder.greaterThan(root.get(expression.getField()), stringValue);
                        } else if (dateValue != null) {
                            predicates[i] = criteriaBuilder.greaterThan(root.get(expression.getField()), dateValue);
                        } else if (numberValue != null) {
                            predicates[i] = criteriaBuilder.gt(root.get(expression.getField()), numberValue);
                        }
                        break;
                    case greaterThanOrEqualTo:
                        if (stringValue != null) {
                            predicates[i] =
                                criteriaBuilder.greaterThanOrEqualTo(root.get(expression.getField()), stringValue);
                        } else if (dateValue != null) {
                            predicates[i] =
                                criteriaBuilder.greaterThanOrEqualTo(root.get(expression.getField()), dateValue);
                        } else if (numberValue != null) {
                            predicates[i] = criteriaBuilder.le(root.get(expression.getField()), numberValue);
                        }
                        break;
                    case lessThan:
                        if (stringValue != null) {
                            predicates[i] = criteriaBuilder.lessThan(root.get(expression.getField()), stringValue);
                        } else if (dateValue != null) {
                            predicates[i] = criteriaBuilder.lessThan(root.get(expression.getField()), dateValue);
                        } else if (numberValue != null) {
                            predicates[i] = criteriaBuilder.lt(root.get(expression.getField()), numberValue);
                        }
                        break;
                    case lessThanOrEqualTo:
                        if (stringValue != null) {
                            predicates[i] =
                                criteriaBuilder.lessThanOrEqualTo(root.get(expression.getField()), stringValue);
                        } else if (dateValue != null) {
                            predicates[i] =
                                criteriaBuilder.lessThanOrEqualTo(root.get(expression.getField()), dateValue);
                        } else if (numberValue != null) {
                            predicates[i] = criteriaBuilder.le(root.get(expression.getField()), numberValue);
                        }
                        break;
                    default:
                        break;
                }
                if(compareType.isNegated()) {
                    predicates[i] = predicates[i].not();
                }
			}
			return criteriaBuilder.and(predicates);
		}
		return query.getRestriction();
	}

	public List<CompareExpression> getCriteriaList() {
		return criteriaList;
	}
}
